package com.auth.Sercurity.TreadDemo;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @author hujian
 * @version 1.0
 * @description: 线程模式售票
 * @date 2022/2/22 15:29
 */
public  class TicketDemo {

    protected static List<Integer> list=new ArrayList<>();

    static class  TicketThread extends Thread{
        private static Integer tickets=100;
        private static final ReentrantLock lock=new ReentrantLock();
        private  Object objectLock=new Object();
        @Override
        public void run(){
            while (tickets>0){
//                selaTicketLock();
//                或者选择Synchornized方法
                selaTicketSychronyzed();
            }
            System.out.println("==========");
            System.out.println(Thread.currentThread().getName()+"售票结束");
        }

        private void selaTicketLock() {
            //                代码加锁
            lock.lock();
            try {
//                中断lock
//                lock.tryLock(
//
//                2, TimeUnit.SECONDS);
//                lock.lockInterruptibly();
                if (tickets>0){
                    System.out.println(Thread.currentThread().getName() + "-" + "卖出最后第" + tickets + "张票");
                    list.add(tickets);
                    tickets--;
                }
            }catch (Exception e){
                System.out.println(e.getMessage());
            }finally {
//                必须手动解锁
                lock.unlock();
            }
        }


        /**
         * 如果是实例方法，那么默认的同步监听对象就是this，
         * 如果是静态方法，默认的同步监听对象是类的字节码对象;
         * 1.实例方法 private synchronized void sellTicketSyn(),默认的同步监听对象就是this,只能锁住同一个对象,在当前使用会出现重复售票;
         * 2.静态方法 private static synchronized void sellTicketSyn(),同步监听对象是类的字节码对象,相当于全局锁;
         */
        private void selaTicketSychronyzed() {
            /**
             * 1.synchronized锁住的是括号里得对象，而不是代码
             * 2.常用的锁对象（this,字节码）
             *      a.当synchronized (TicketThread.class),程序正常执行，只有一份字节码，谁拥有字节码就有执行权
             *      b.当chnchronized(objectLock)或chnchronized(this),this代表当前对象，启动线程的时候看的实例了几次TicketThread 类，如果
             *           TicketThread ticketThread = new TicketThread();
             *         Thread thread1=new Thread(ticketThread,"窗口1");
             *         Thread thread2=new Thread(ticketThread,"窗口2");
             *         那么只会有一个objectLock对象或者TicketThread对象。如果
             *          Thread  thread1= new TicketThread();
             *         Thread  thread2= new TicketThread();
             *         那么就会多个objectLock对象或者TicketThread对象，也会重复卖票
             *      c.synchronized（tickets），因为数量是变化的，所以当存在数量不相等时会同时进行。
             *
             */
            synchronized (this){
                if (tickets>0){
                    System.out.println(Thread.currentThread().getName() + "-" + "卖出最后第" + tickets + "张票");
                    list.add(tickets);
                    tickets--;
                }
            }
        }
    }


    public static void main(String[] args) {
        TicketThread ticketThread = new TicketThread();
        Thread thread1=new Thread(ticketThread,"窗口1");
        Thread thread2=new Thread(ticketThread,"窗口2");
        Thread thread3=new Thread(ticketThread,"窗口3");

//        Thread  thread1= new TicketThread();
//        Thread  thread2= new TicketThread();
//        Thread  thread3= new TicketThread();
//        thread1.setName("窗口1");
//        thread2.setName("窗口2");
//        thread3.setName("窗口3");
        System.out.println("开始售票");
        System.out.println("=================");
        thread1.start();
        thread2.start();
        thread3.start();
        List<Integer> result=new ArrayList<>();

        Map<Integer, Long> collect = list.stream().collect(Collectors.groupingBy(e -> e, Collectors.counting()));
        collect.forEach((a,b)->{
            if (b>1){
                result.add(a);
            }
        });
        System.out.println(result.toString());
        System.out.println(list.toString());
    }
}
